package grammar.parser;

import error.processing.MyErrorProcessing;
import execution.Executable;
import grammar.parser.stmt.Block;
import grammar.parser.stmt.ReturnStmt;
import semantic.parser.SymbolTableElement;
import word.parser.WordElement;

import java.util.List;
import java.util.Objects;

public class FuncDef extends GrammarParser implements Executable {
    private final SymbolTableElement.SymbolType funcType;
    private final String ident;
    private final FuncFParams params;
    private Block block;


    public FuncDef(SymbolTableElement.SymbolType type, String ident, FuncFParams params) {
        assert type == SymbolTableElement.SymbolType.INT_FUNC ||
                type == SymbolTableElement.SymbolType.VOID_FUNC;
        this.funcType = type;
        this.ident = ident;
        this.params = params;
    }

    public void setBlock(Block block) {
        this.block = block;
    }

    public static FuncDef buildFuncDef() {
        assert getAnalyzingWord().getType() == WordElement.WordType.INTTK ||
                getAnalyzingWord().getType() == WordElement.WordType.VOIDTK;
        SymbolTableElement.SymbolType type;
        if (getAnalyzingWord().getType() == WordElement.WordType.INTTK) {
            type = SymbolTableElement.SymbolType.INT_FUNC;
            MyErrorProcessing.haveReturn();
        } else {
            type = SymbolTableElement.SymbolType.VOID_FUNC;
            MyErrorProcessing.withoutReturn();
        }
        addScope();

        analyzeNextWord();
        assert getAnalyzingWord().getType() == WordElement.WordType.IDENFR;
        String ident = getAnalyzingWord().getWord();

        FuncFParams params = new FuncFParams();
        int identPosition = getNowPosition();
        analyzeNextWord();
        assert getAnalyzingWord().getType() == WordElement.WordType.LPARENT;
        analyzeNextWord();
        if (getAnalyzingWord().getType() == WordElement.WordType.INTTK) {
            params = FuncFParams.buildFuncFParams();
        }
        if (getAnalyzingWord().getType() != WordElement.WordType.RPARENT) {
            MyErrorProcessing.buildErrorProcessing(MyErrorProcessing.ErrorType.j,
                    getAnalyzingWord(-1));
        } else {
            analyzeNextWord();
        }

        FuncDef funcDef = new FuncDef(type, ident, params);
        getSemanticParser().declFunc(getWord(identPosition), type, 0, funcDef);

        outScopeWithNotUpdateSymbolTable();

        Block block = Block.buildBlock();
        funcDef.setBlock(block);
        funcDef.returnCheck();

        return funcDef;
    }


    public void returnCheck() {
        if (this.funcType == SymbolTableElement.SymbolType.INT_FUNC) {
            List<BlockItem> blockItemList = block.getBlockItemList();

            if (blockItemList.size() < 1 ||
                    !(blockItemList.get(blockItemList.size() - 1) instanceof ReturnStmt)) {
                MyErrorProcessing.buildErrorProcessing(MyErrorProcessing.ErrorType.g,
                        getAnalyzingWord(-1));
            }
        }
    }

    public boolean isVoid() {
        return this.funcType == SymbolTableElement.SymbolType.VOID_FUNC;
    }

    public void paramsCheck(List<FuncFParams.FuncFParam> list, int identPosition) {
        if (list.size() != params.getParams().size()) {
            MyErrorProcessing.buildErrorProcessing(MyErrorProcessing.ErrorType.d,
                    getWord(identPosition));
        } else {
            for (int i = 0; i < list.size(); ++i) {
                if (!list.get(i).equals(params.getParams().get(i))) {
                    MyErrorProcessing.buildErrorProcessing(MyErrorProcessing.ErrorType.e,
                            getWord(identPosition));
                    break;
                }
            }
        }
    }

    public String getIdent() {
        return ident;
    }

    public FuncFParams getParams() {
        return params;
    }

    public SymbolTableElement.SymbolType getFuncType() {
        return funcType;
    }

    @Override
    public void execute() {
        block.execute();
        getSemanticParser().finishReturn();
    }
}
